﻿using Newtonsoft.Json;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using SuperX.Common;
using SuperX.Common.MSG;
using SuperX.Common.Respository;
using SuperX.Common.Collection;
using SuperX.Common.PlugIn;
using SuperX.Common.APIManager;
using SuperX.Common.Scheduler;
using System.Linq;
using SuperX.Common.Log;
using SuperX.Common.Object;
using SuperX.Common.BasicService;
using SuperX.Common.Helper;
using log4net.Plugin;
using System.ComponentModel.Design;
using SuperX.Common.Code;

namespace SuperX.ProjectCore
{
    //
    public class Project : BasicObjCollection<IPlugIns>, IProject, IObjCollection, IBasicObject, ICollectionNotify, IController, IPersistence
    {
        //
        public event EventHandler<ProjectActionEventArgs> ProjectAction;

        //
        public string ProjectDir { get; internal set; }

        //
        public string ProjectFullPath { get; internal set; }

        //
        public IProjService Server
        {
            get
            {
                return base.Parent as ProjService;
            }
        }

        //
        public ProjectState State { get; private set; }

        //
        public DateTime StartTime { get; private set; }

        //
        public SchedulerManager SchedulerManager
        {
            get
            {
                SchedulerManager result;
                if ((result = this._dispatcherManager) == null)
                {
                    result = (this._dispatcherManager = new SchedulerManager());
                }
                return result;
            }
        }

        public ConcurrentDictionary<string, IBasicObject> Objects =>
                    _Objects ?? (_Objects = new ConcurrentDictionary<string, IBasicObject>());

        //
        public ConcurrentDictionary<string, List<IBasicObject>> CacheObjects
        {
            get
            {
                ConcurrentDictionary<string, List<IBasicObject>> result;
                if ((result = this._cacheObjects) == null)
                {
                    result = (this._cacheObjects = new ConcurrentDictionary<string, List<IBasicObject>>());
                }
                return result;
            }
        }

        //
        public Dictionary<string, IAPIController> APIControllers
        {
            get
            {
                return (from m in this.Objects
                        where m.Value is IAPIController
                        select m).ToDictionary((KeyValuePair<string, IBasicObject> m) => m.Key, (KeyValuePair<string, IBasicObject> m) => m.Value as IAPIController);
            }
        }

        //
        public ObjectCollection<IProject, IPlugIns> PlugIns
        {
            get
            {
                ObjectCollection<IProject, IPlugIns> result;
                if ((result = this._plugIns) == null)
                {
                    result = (this._plugIns = new ObjectCollection<IProject, IPlugIns>(this));
                }
                return result;
            }
            set
            {
                this._plugIns = value;
                ObjectCollection<IProject, IPlugIns> plugIns = this._plugIns;
                if (plugIns == null)
                {
                    return;
                }
                plugIns.SetParent(this);
            }
        }

        //
        public bool IsRunning { get; private set; }

        //
        public ControllerStatus Status { get; private set; }

        //
        [JsonIgnore]
        public IRepository Repository { get; set; }

        //
        public ProjectInfo ProjectInfo
        {
            get
            {
                return this._projectInfo;
            }
            set
            {
                this._projectInfo = value;
                ProjectInfo projectInfo = this._projectInfo;
                this.Name = ((projectInfo != null) ? projectInfo.Name : null);
                ProjectInfo projectInfo2 = this._projectInfo;
                base.Description = ((projectInfo2 != null) ? projectInfo2.Description : null);
            }
        }

        //
        public IReference References { get; set; }

        //
        public IMsgCache MsgCache { get; set; }

        //
        //public IPubSubManager PubSubManager { get; set; }

        public CodeObject CodeObject { get; set; }

        //
        public bool IsLoading { get; private set; }

        public CodeManager CodeManager { get; set; }

        //
        public Project(IProjService server)
        {
            this.MsgCache = new MsgCache(this);
            this.MsgCache.Reset();
            //this.PubSubManager = new PubSubManager(this);
            this.Status = ControllerStatus.None;
            this._manualResetEventSlim = new ManualResetEventSlim(true);
            base.Parent = server;
            this.State = ProjectState.Unknown;
            CodeObject = new CodeObject(this, "iot");
            CodeManager = new CodeManager();
        }

        //
        public bool Init()
        {
            this.Status = ControllerStatus.Initing;
            bool result = true;
            foreach (IPlugIns plugIn in this.PlugIns)
            {
                try
                {
                    result = plugIn.Init();
                }
                catch (Exception exception)
                {
                    plugIn.IsEnable = false;
                    Logger.ErrorException(exception);
                }
            }
            return result;
        }

        //
        private bool RunOnce(IBasicObject baseObject)
        {
            object propertyValue = baseObject.GetPropertyValue("RunOnce");
            return propertyValue is bool && (bool)propertyValue;
        }

        //
        public bool Start()
        {
            if (this.Server.RunMode == SuperX.Common.ServiceRunMode.Designer || this.IsRunning)
            {
                return false;
            }
            this.State = this.ProjectInfo.ProjectState;
            //this.PubSubManager.Start();
            this.MsgCache.EnableListen();
            this._manualResetEventSlim.Reset();
            this.Status = ControllerStatus.Starting;
            this.StartTime = DateTime.Now;
            EventHandler<ProjectActionEventArgs> projectAction = this.ProjectAction;
            if (projectAction != null)
            {
                projectAction(this, new ProjectActionEventArgs("before-start"));
            }
            bool result;
            try
            {
                foreach (IPlugIns plugIn in this.PlugIns)
                {
                    if (plugIn.IsEnable && (this.State != ProjectState.Standby || this.RunOnce(plugIn) || !(plugIn.GetType().Name != "DataServer")))
                    {
                        this._pluginAtomicOperEvent.Reset();
                        plugIn.Start();
                        this._pluginAtomicOperEvent.Set();
                        if (this.Status == ControllerStatus.Stopping)
                        {
                            return false;
                        }
                    }
                }
                this.IsRunning = true;
                this.Status = ControllerStatus.Running;
                result = true;
            }
            catch (Exception error)
            {
                if (this.Status == ControllerStatus.Stopping)
                {
                    result = false;
                }
                else
                {
                    IProjService server = base.Parent as IProjService;
                    if (server != null)
                    {
                        server.NotifyError(error, false);
                    }
                    result = false;
                }
            }
            finally
            {
                EventHandler<ProjectActionEventArgs> projectAction2 = this.ProjectAction;
                if (projectAction2 != null)
                {
                    projectAction2(this, new ProjectActionEventArgs("started"));
                }
                this._pluginAtomicOperEvent.Set();
                this._manualResetEventSlim.Set();
            }
            return result;
        }

        //
        public bool Stop()
        {
            if (this.Server.RunMode == SuperX.Common.ServiceRunMode.Designer)
            {
                return false;
            }
            this.Status = ControllerStatus.Stopping;
            EventHandler<ProjectActionEventArgs> projectAction = this.ProjectAction;
            if (projectAction != null)
            {
                projectAction(this, new ProjectActionEventArgs("before-stop"));
            }
            bool result;
            try
            {
                for (int i = this.PlugIns.Count - 1; i >= 0; i--)
                {
                    IPlugIns plugIn = this.PlugIns[i];
                    try
                    {
                        plugIn.Stop();
                    }
                    catch (Exception exception)
                    {
                        Logger.Exception(exception);
                    }
                }
                this.SchedulerManager.Stop();
                this.IsRunning = false;
                result = true;
            }
            catch (Exception error)
            {
                this.IsRunning = false;
                IProjService server = base.Parent as IProjService;
                if (server != null)
                {
                    server.NotifyError(error, false);
                }
                result = false;
            }
            finally
            {
                EventHandler<ProjectActionEventArgs> projectAction2 = this.ProjectAction;
                if (projectAction2 != null)
                {
                    projectAction2(this, new ProjectActionEventArgs("stopped"));
                }
                this.Status = ControllerStatus.None;
                this._manualResetEventSlim.Wait(22000);
                this.MsgCache.UnEnableListen();
                //this.PubSubManager.Stop();
            }
            return result;
        }

        //
        public bool Load(string fileName)
        {
            this.ProjectFullPath = Path.GetFullPath(fileName);
            this.ProjectDir = Path.GetDirectoryName(this.ProjectFullPath);
            if (!File.Exists(this.ProjectFullPath) || Path.GetExtension(fileName) != ".proj")
            {
                IProjService server = this.Server;
                if (server != null)
                {
                    server.NotifyError(new Exception(string.Format("\"{0}\" 项目文件不存在", this.ProjectFullPath)), true);
                }
                return false;
            }
            this.Repository = new JsonRepository(this.ProjectFullPath);
            this.Repository.Load(typeof(ProjectInfo));
            this.ProjectInfo = this.CheckDirectorySeparatorChar(this.Repository.Model as ProjectInfo);
            if (this.ProjectInfo == null)
            {
                IProjService server2 = this.Server;
                if (server2 != null)
                {
                    server2.NotifyError(new Exception(string.Format("\"{0}\" 项目加载失败", this.ProjectFullPath)), true);
                }
                return false;
            }
            this.LoadPlugIns();
            Logger.Info(string.Format("加载 \"{0}\" 成功", this.ProjectFullPath));
            return true;
        }

        //
        private ProjectInfo CheckDirectorySeparatorChar(ProjectInfo projectInfo)
        {
            return projectInfo;
        }

        //
        public ProjectInfo LoadProjectInfo(string fileName)
        {
            this.ProjectFullPath = Path.GetFullPath(fileName);
            if (!File.Exists(this.ProjectFullPath) || Path.GetExtension(fileName) != ".proj")
            {
                return null;
            }
            this.Repository = new JsonRepository(this.ProjectFullPath);
            this.Repository.Load(typeof(ProjectInfo));
            return this.Repository.Model as ProjectInfo;
        }

        //
        internal void LoadPlugIns()
        {
            bool flag = false;
            List<Task> list = new List<Task>();
            this.IsLoading = true;
            try
            {
                Stopwatch stopwatch = new Stopwatch();
                stopwatch.Start();
                BasicObject.ObjectInstances.Add(BasicServiceManager.GetProjService());
                var projectCfg = (from m in this.ProjectInfo.Services
                                  orderby m.RunIndex
                                  select m).ToList();
                var localService = LoadLocalService();
                foreach (var item in localService)
                {
                    var exists = projectCfg.Where(a => a.ServiceType == item.ServiceType).FirstOrDefault();
                    if (exists == null)
                    {
                        projectCfg.Add(item);
                    }
                }
                projectCfg = projectCfg.OrderBy(a => a.RunIndex).ToList();
                foreach (var item in projectCfg)
                {
                    ModuleInfo plugInConfig = item;
                    flag = this.CheckModuleName(plugInConfig);
                    IPlugIns plugIn = this.CreatePlugIn(plugInConfig);
                    if (plugIn == null)
                    {
                        Logger.Error(string.Format("加载\"{0}\"插件错误", plugInConfig.Name));
                    }
                    else
                    {
                        list.Add(Task.Factory.StartNew<bool>(() => plugIn.Load(Path.Combine(this.ProjectDir, plugInConfig.CfgName))));
                        this.PlugIns.Add(plugIn);
                    }
                }

                Task.WaitAll(list.ToArray());
                this.AddObject();
                stopwatch.Stop();
                Logger.Trace(stopwatch.ElapsedMilliseconds.ToString());
                if (flag)
                {
                    try
                    {
                        this.Save();
                    }
                    catch (Exception exception)
                    {
                        Logger.WarnException(exception);
                    }
                }
            }
            catch (Exception exception2)
            {
                Logger.ErrorException(exception2);
            }
            finally
            {
                this.IsLoading = false;
            }
        }

        public List<ModuleInfo> LoadLocalService()
        {
            List<string> files = new List<string>();
            string rootDirectory = AppDomain.CurrentDomain.BaseDirectory;
            files.AddRange(Directory.GetFiles(rootDirectory));
            var servicePath = Path.Combine(rootDirectory, "Services");
            if (Directory.Exists(servicePath))
            {
                files.AddRange(Directory.GetFiles(servicePath));
            }
            List<ModuleInfo> modules = new List<ModuleInfo>();
            foreach (var file in files)
            {
                var filenameLow = Path.GetFileName(file).ToLower();
                if (Path.GetExtension(file).ToLower() == ".dll" && filenameLow.Contains("service") && filenameLow.StartsWith("superx") && !filenameLow.Equals("super.projectcore"))
                {
                    Assembly assembly = Assembly.LoadFile(file); // 加载程序集
                    try
                    {
                        // 获取所有类型
                        Type[] types = assembly.GetTypes();
                        foreach (Type type in types)
                        {
                            if (type.BaseType == typeof(HostService))
                            {
                                int runIndex = 99;
                                PropertyInfo property = type.GetProperty("RunIndex");
                                if (property != null)
                                {
                                    runIndex = System.Convert.ToInt32(property.GetValue(null, null)); // 传入实例对象，如果属性是静态的，则传入null
                                }
                                var manifestModule = assembly.ManifestModule;
                                var serviceName = manifestModule.Name.Replace(".dll", "").Split('.').LastOrDefault();
                                ModuleInfo module = new ModuleInfo()
                                {
                                    Name = type.Name,
                                    IsEnable = true,
                                    CfgName = $"Services/{serviceName}.cfg",
                                    ServiceType = $"{type.FullName},{manifestModule}",
                                    RunIndex = runIndex,
                                };
                                modules.Add(module);
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        Logger.WarnException(ex);
                    }
                }
            }
            return modules;
        }

        //
        private void AddObject()
        {
            for (int i = 0; i < BasicObject.ObjectInstances.Count; i++)
            {
                IBasicObject baseObject = BasicObject.ObjectInstances[i];
                if (baseObject != null)
                {
                    bool flag = false;
                    if (baseObject != null)
                    {
                        flag = this.Objects.TryAdd(baseObject.FullName, baseObject);
                    }
                    if (flag)
                    {
                        this.MsgCache.Listen(baseObject);
                    }
                }
            }
            BasicObject.ObjectInstances.Clear();
        }

        //
        private bool CheckModuleName(ModuleInfo plugInConfig)
        {
            bool result = false;
            if (object.Equals(plugInConfig.Name, "AlarmLink"))
            {
                plugInConfig.Name = "AlarmServer";
                string text = Path.Combine(this.ProjectDir, plugInConfig.CfgName);
                plugInConfig.CfgName = Path.Combine("Services", "Alarm.scfg");
                string text2 = Path.Combine(this.ProjectDir, plugInConfig.CfgName);
                if (File.Exists(text) && !object.Equals(text, text2))
                {
                    try
                    {
                        File.Copy(text, text2);
                        File.Delete(text);
                    }
                    catch (Exception exception)
                    {
                        Logger.WarnException(exception);
                    }
                }
                result = true;
            }
            else if (object.Equals(plugInConfig.Name, "TagLink"))
            {
                plugInConfig.Name = "TagServer";
                result = true;
            }
            return result;
        }

        //
        private IPlugIns CreatePlugIn(ModuleInfo moduleInfo)
        {
            try
            {
                var plugIn = CreateInstanceUtil.CreateInstance<IPlugIns>(moduleInfo.ServiceType);
                if (plugIn != null)
                {
                    plugIn.Name = moduleInfo.Name;
                    plugIn.IsEnable = moduleInfo.IsEnable;
                    plugIn.Description = moduleInfo.Description;
                    plugIn.RunMode = moduleInfo.RunMode;
                    moduleInfo.ContextType = String.Empty;
                    plugIn.ModuleContext =
                        CreateInstanceUtil.CreateInstance<ModuleContext>(moduleInfo.ContextType) ??
                        new ModuleContext { Host = this, HostDir = ProjectDir };
                    if (plugIn.FileName == null)
                    {
                        plugIn.FileName = moduleInfo.CfgName;
                    }
                }
                return plugIn;
            }
            catch (Exception ex)
            {
                Logger.Error(ex.ToString());
                return null;
            }
        }

        /// <summary>
        /// 保存项目
        /// </summary>
        /// <returns></returns>
        public bool Save()
        {
            if (string.IsNullOrWhiteSpace(this.ProjectFullPath) || Path.GetExtension(this.ProjectFullPath) != ".proj")
            {
                return false;
            }
            foreach (IPlugIns plugIn in this.PlugIns)
            {
                plugIn.Save();
            }
            if (this.Repository == null)
            {
                this.Repository = new JsonRepository(this.ProjectFullPath);
            }
            this.Repository.Model = this.ProjectInfo;
            this.Repository.Save();
            Logger.Info(string.Format("保存 \"{0}\" 成功", this.ProjectFullPath));
            return true;
        }

        /// <summary>
        /// 项目另存为
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public bool SaveAs(string fileName)
        {
            if (string.IsNullOrWhiteSpace(fileName))
            {
                return false;
            }
            if (this.Repository == null)
            {
                this.Repository = new JsonRepository(fileName);
            }
            this.Repository.Model = this.ProjectInfo;
            this.Repository.Save();
            return true;
        }

        /// <summary>
        /// 退出项目
        /// </summary>
        /// <returns></returns>
        public bool Exit()
        {
            this.Objects.Clear();
            this.CacheObjects.Clear();
            this.PlugIns.Clear();
            Logger.Info(string.Format("退出 \"{0}\" 成功", this.ProjectFullPath));
            return true;
        }

        //
        public override List<string> GetKeys()
        {
            return this.PlugIns.Keys;
        }

        //
        public override IPlugIns this[string name]
        {
            get
            {
                return this.PlugIns[name];
            }
        }

        //
        public override List<IPlugIns> GetValues()
        {
            return this.PlugIns.Values;
        }

        //
        public override bool IsUniqueName(string name, IObjCollection parent)
        {
            if (parent != null)
            {
                return parent.IsUniqueName(name, null);
            }
            return this.PlugIns[name] == null;
        }

        //
        public override void Dispose()
        {
            //this.PubSubManager.Dispose();
            this.Stop();
            this.Exit();
            UidResource.Clear();
            base.Dispose();
        }

        //
        public void FireEventChanged(IBasicObject baseObject, ITimeData eventArgs, string source, Type dataType = null, string fullName = null)
        {
            if (eventArgs == null)
            {
                return;
            }
            string text = fullName ?? ((baseObject != null) ? baseObject.FullName : null);
            if (text == null)
            {
                return;
            }
            TimeDataMsg msg = new TimeDataMsg
            {
                FullName = text,
                MsgSource = source,
                TimeStamp = eventArgs.TimeStamp,
                TimeData = eventArgs,
                Object = baseObject,
                DataType = dataType
            };
            base.OnNotifyMessage(msg);
        }

        public void FireEventChanged(IBasicObject baseObject, string source, Type dataType = null, string fullName = null)
        {
            throw new NotImplementedException();
        }

        public ICode CreateEngine(string engineName, IDictionary<string, object> variables, string engineType = "Python")
        {
            if (engineType == "Python")
            {
                if (variables == null)
                {
                    variables = new Dictionary<string, object>();
                }
                if (!variables.ContainsKey(CodeObject.Name))
                {
                    variables.Add(CodeObject.Name, CodeObject);
                }
                foreach (var command in APIControllers)
                {
                    if (!variables.ContainsKey(command.Key))
                    {
                        variables.Add(command.Key, command.Value);
                    }
                }
                if (!variables.ContainsKey(nameof(loglevel)))
                {
                    variables.Add(nameof(loglevel), new loglevel());
                }
                var engine = new PythonCode(engineName, variables);
                //ScriptManager.TryAdd(engineName, engine);
                return engine;
            }
            else
            {
                throw new NotSupportedException($"not support {engineType} type");
            }
        }

        private ConcurrentDictionary<string, IBasicObject> _Objects;
        private ConcurrentDictionary<string, List<IBasicObject>> _cacheObjects;
        private ObjectCollection<IProject, IPlugIns> _plugIns;
        private ProjectInfo _projectInfo;
        private SchedulerManager _dispatcherManager;
        private ManualResetEventSlim _manualResetEventSlim;
        private const int StopWaitTime = 22000;
        private ManualResetEventSlim _pluginAtomicOperEvent = new ManualResetEventSlim(true);
    }

    public class loglevel
    {
        public const string Debug = "Debug";

        public const string Info = "Info";

        public const string Trace = "Trace";

        public const string Warn = "Warn";

        public const string Error = "Error";
    }
}